// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 此文件实现FindExportData。

package gcimporter

import (
	"bufio"
	"fmt"
	"io"
	"strconv"
	"strings"
)

func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
	// 请参见$GOROOT/include/ar.h.
	hdr := make([]byte, 16+12+6+6+8+10+2)
	_, err = io.ReadFull(r, hdr)
	if err != nil {
		return
	}
	// 继续调试
	if false {
		fmt.Printf("header: %s", hdr)
	}
	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
	size, err = strconv.Atoi(s)
	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
		err = fmt.Errorf("invalid archive header")
		return
	}
	name = strings.TrimSpace(string(hdr[:16]))
	return
}

// FindExportData将读取器r定位在
// 通过读取导出底层GC创建的对象/存档
// 文件的数据部分。在调用此函数之前，读取器必须位于文件的
// 开头。hdr结果
// 是导出数据之前的字符串，可以是“$$”或“$$B”。
// 
func FindExportData(r *bufio.Reader) (hdr string, err error) {
	// 读取第一行以确保这是一个目标文件。
	line, err := r.ReadSlice('\n')
	if err != nil {
		err = fmt.Errorf("can't find export data (%v)", err)
		return
	}

	if string(line) == "!<arch>\n" {
		var name string
		if name, _, err = readGopackHeader(r); err != nil {
			return
		}

		// 第一个条目应该是_;。PKGDEF。
		if name != "__.PKGDEF" {
			err = fmt.Errorf("go archive is missing __.PKGDEF")
			return
		}

		// 阅读_;的第一行。PKGDEF数据，因此
		// 行再次成为输入的第一行。现在在_;。PKGDEF在存档中或仍在文件开头。
		if line, err = r.ReadSlice('\n'); err != nil {
			err = fmt.Errorf("can't find export data (%v)", err)
			return
		}
	}

	// 无论哪种方式，行都应该以“go object”开头。
	if !strings.HasPrefix(string(line), "go object ") {
		err = fmt.Errorf("not a Go object file")
		return
	}

	// 跳过对象头导出数据。
	// 在以$$开头的第一行之后开始。
	for line[0] != '$' {
		if line, err = r.ReadSlice('\n'); err != nil {
			err = fmt.Errorf("can't find export data (%v)", err)
			return
		}
	}
	hdr = string(line)

	return
}
